import json
import random
import math
import os
import numpy as np
from typing import List, Dict, Any

__all__ = ['MaimaiChartGenerator', 'generate_chart_with_audio_features', 
           'generate_chart_from_features', 'ChartValidator', 'PremiumMaimaiChartGenerator',
           'generate_premium_chart']

class ChartSegment:
    """谱面段落类"""
    def __init__(self, notes: list, bpm: float = None, metadata: dict = None):
        self.notes = notes
        self.bpm = bpm
        self.metadata = metadata or {}
        self.frequency = 1
        self.source_charts = []  # 来源谱面
        self.vector = None  # 向量表示
        self.normalized_notes = None  # 标准化后的音符
        
class MaimaiChartGenerator:
    """
    用于生成舞萌DX谱面的类
    根据学习到的《认知神经谱面学 v9.3》理论进行优化
    """
    def __init__(self, bpm=120.0):
        self.bpm = bpm
        self.lines = []
        # 添加谱面头部信息
        self.header = [
            f"&title=AI Generated Chart",
            f"&artist=AI Composer",
            f"&wholebpm={bpm}",
            "&first=0",
            "&des=Generated by AI from audio features"
        ]
        self.current_time = 0.0  # 添加当前时间跟踪
        self.multi_tap_history = []  # 记录多押历史，用于控制频率
        self.hold_state = {}  # 记录每个按键的Hold状态 {button: end_time}
        self.section_patterns = []  # 记录已使用的模式，避免重复
        self.emotional_state = "neutral"  # 情感状态：neutral, gentle, intense, chaotic
        self.breath_count = 0  # 呼吸计数器
        self.touch_symbols_used = []  # 已使用的触觉符号
        
    def generate_single_note(self):
        """
        生成单个音符，根据情感状态调整生成策略
        """
        # 确定哪些按键当前没有Hold
        available_buttons = []
        for button in range(1, 9):
            if button not in self.hold_state or self.hold_state[button] <= self.current_time:
                available_buttons.append(button)
        
        # 如果没有可用按键，只能生成空音符
        if not available_buttons:
            return ""
        
        position = str(random.choice(available_buttons))
        
        # 根据情感状态调整音符类型权重
        if self.emotional_state == "gentle":
            # 温柔状态：增加Hold和触觉符号概率
            note_type = random.choices(
                ['basic', 'hold', 'slide', 'break', 'multi'], 
                weights=[30, 25, 15, 5, 25]
            )[0]
        elif self.emotional_state == "intense":
            # 激烈状态：增加Break和Slide概率
            note_type = random.choices(
                ['basic', 'hold', 'slide', 'break', 'multi'], 
                weights=[20, 10, 20, 15, 35]
            )[0]
        elif self.emotional_state == "chaotic":
            # 混乱状态：增加多押和复杂符号概率
            note_type = random.choices(
                ['basic', 'hold', 'slide', 'break', 'multi'], 
                weights=[15, 5, 15, 10, 55]
            )[0]
        else:
            # 中性状态：平衡分布
            note_type = random.choices(
                ['basic', 'hold', 'slide', 'break', 'multi'], 
                weights=[40, 15, 10, 5, 30]
            )[0]
        
        # 检查是否可以生成Hold（按键必须是可用的）
        if note_type == 'hold' and int(position) in available_buttons:
            # Hold音符，根据情感状态选择类型
            if self.emotional_state == "gentle":
                # 温柔状态：使用较长的Hold，模拟"被凝视时的窒息"
                hold_types = [(8, 1), (8, 2), (12, 1), (16, 1), (16, 2), (24, 1)]
            elif self.emotional_state == "intense":
                # 激烈状态：使用中等长度的Hold
                hold_types = [(4, 1), (8, 1), (8, 2), (12, 1)]
            else:
                # 其他状态：随机选择
                hold_types = [(4, 1), (8, 1), (8, 2), (12, 1), (16, 1), (16, 2), (24, 1)]
            
            hold_type = random.choice(hold_types)
            hold_beats = hold_type[0] / 4.0  # 将note值转换为拍数
            hold_end_time = self.current_time + hold_beats * (60.0 / self.bpm)
            # 记录Hold状态
            self.hold_state[int(position)] = hold_end_time
            return f"{position}h[{hold_type[0]}:{hold_type[1]}]"
            
        elif note_type == 'slide':
            # Slide音符，根据情感状态选择类型
            if self.emotional_state == "gentle":
                # 温柔状态：使用柔和的滑动符号
                slide_types = ['-', 's']
            elif self.emotional_state == "intense":
                # 激烈状态：使用强烈的滑动符号
                slide_types = ['>', '<']
            else:
                # 其他状态：随机选择
                slide_types = ['-', 's', '>', '<']
                
            slide_type = random.choice(slide_types)
            # 确保目标键与起始键不同且可用
            target_options = [b for b in available_buttons if str(b) != position]
            if target_options:
                target = str(random.choice(target_options))
            else:
                target = str(random.choice(available_buttons))
                
            # 根据情感状态选择持续时间
            if self.emotional_state == "gentle":
                slide_durations = [(8, 1), (8, 2), (12, 1), (16, 1)]
            elif self.emotional_state == "intense":
                slide_durations = [(4, 1), (8, 1), (8, 2)]
            else:
                slide_durations = [(4, 1), (8, 1), (8, 2), (12, 1), (16, 1), (16, 2)]
                
            duration = random.choice(slide_durations)
            return f"{position}{slide_type}{target}[{duration[0]}:{duration[1]}]"
            
        elif note_type == 'break':
            # Break音符
            if random.random() < 0.5:
                return position + "b"
            else:
                # Break Hold
                if self.emotional_state == "gentle":
                    hold_types = [(8, 1), (8, 2), (12, 1), (16, 1)]
                elif self.emotional_state == "intense":
                    hold_types = [(4, 1), (8, 1), (8, 2), (12, 1)]
                else:
                    hold_types = [(4, 1), (8, 1), (8, 2), (12, 1), (16, 1)]
                    
                hold_type = random.choice(hold_types)
                hold_beats = hold_type[0] / 4.0  # 将note值转换为拍数
                hold_end_time = self.current_time + hold_beats * (60.0 / self.bpm)
                # 记录Hold状态
                self.hold_state[int(position)] = hold_end_time
                return f"{position}bh[{hold_type[0]}:{hold_type[1]}]"
                
        elif note_type == 'multi':
            # 多押音符
            # 确保有足够的可用按键
            if len(available_buttons) >= 2:
                # 根据情感状态控制多押数量
                if self.emotional_state == "chaotic":
                    # 混乱状态下可以生成更多键的多押
                    max_keys = min(4, len(available_buttons))
                elif self.emotional_state == "gentle":
                    # 温柔状态下限制多押数量
                    max_keys = min(3, len(available_buttons))
                else:
                    max_keys = min(3, len(available_buttons))
                    
                positions = random.sample(available_buttons, random.randint(2, max_keys))
                return "/".join(map(str, positions))
            else:
                # 可用按键不足，改为单押
                return position
                
        else:
            # 基本点击音符
            if random.random() < 0.3 and len(available_buttons) >= 2:
                # 多押音符
                if self.emotional_state == "chaotic":
                    max_keys = min(4, len(available_buttons))
                else:
                    max_keys = min(3, len(available_buttons))
                    
                positions = random.sample(available_buttons, random.randint(2, max_keys))
                return "/".join(map(str, positions))
            else:
                return position

    def generate_breath_note(self):
        """
        生成呼吸音符 {1}，模拟"呼吸确认"
        """
        self.breath_count += 1
        # 确定哪些按键当前没有Hold
        available_buttons = []
        for button in range(1, 9):
            if button not in self.hold_state or self.hold_state[button] <= self.current_time:
                available_buttons.append(button)
        
        if available_buttons:
            position = str(random.choice(available_buttons))
            return position
        else:
            return ""

    def generate_touch_symbol(self):
        """
        生成触觉符号，模拟"指尖滑过脸颊的温度"等触觉体验
        """
        # 确定哪些按键当前没有Hold
        available_buttons = []
        for button in range(1, 9):
            if button not in self.hold_state or self.hold_state[button] <= self.current_time:
                available_buttons.append(button)
        
        if len(available_buttons) < 2:
            return self.generate_single_note()
        
        # 从已知的触觉符号中选择
        touch_symbol = random.choice([
            "slide_gentle",    # 模拟指尖滑过脸颊
            "hold_warm",       # 模拟被抚摸头发时的颤抖
            "break_swallow",   # 模拟咽下话语的喉结
            "multi_reach",     # 模拟想要触碰却怕被拒绝
            "hold_gaze"        # 模拟被凝视时的窒息
        ])
        
        if touch_symbol not in self.touch_symbols_used:
            self.touch_symbols_used.append(touch_symbol)
        
        position1 = random.choice(available_buttons)
        available_buttons.remove(position1)
        position2 = random.choice(available_buttons)
        
        if touch_symbol == "slide_gentle":
            # 模拟指尖滑过脸颊的温度 - 使用柔和的滑动
            return f"{position1}s{position2}[8:1]"
        elif touch_symbol == "hold_warm":
            # 模拟被抚摸头发时的颤抖 - 使用较长的Hold
            return f"{position1}h[12:1]"
        elif touch_symbol == "break_swallow":
            # 模拟咽下话语的喉结 - 使用Break
            return f"{position1}b"
        elif touch_symbol == "multi_reach":
            # 模拟想要触碰却怕被拒绝 - 使用多押
            return f"{position1}/{position2}"
        elif touch_symbol == "hold_gaze":
            # 模拟被凝视时的窒息 - 使用较长的Hold
            return f"{position1}h[16:2]"
        
        return self.generate_single_note()

    def generate_notes_for_line(self, note_value):
        """
        为一行生成音符
        :param note_value: 音符值
        """
        notes = [""] * note_value
        
        # 控制音符密度，同时确保不会违反同时按键限制
        max_notes = max(note_value, 30)  # 确保最大音符数至少为30
        min_notes = max(note_value // 2, 20)  # 最少放置note_value/2或20个音符
        num_notes = random.randint(min_notes, max_notes)
        
        # 特殊处理：如果当前是{1}模式，生成呼吸音符
        if note_value == 1:
            notes[0] = self.generate_breath_note()
            return notes
        
        # 根据情感状态调整生成策略
        if self.emotional_state == "gentle" and random.random() < 0.4:
            # 温柔状态下有一定概率生成触觉符号
            notes[random.randint(0, note_value-1)] = self.generate_touch_symbol()
        elif self.emotional_state == "chaotic" and random.random() < 0.3:
            # 混乱状态下增加多押概率
            num_notes = min(num_notes + random.randint(5, 10), note_value)
        
        # 确保音符不会在同一时间点违反按键限制（同一时间最多2个键）
        used_slots = set()
        multi_tap_count = 0  # 当前行的多押计数
        
        for _ in range(num_notes):
            # 寻找一个未使用的槽位
            available_slots = [i for i in range(note_value) if i not in used_slots]
            if not available_slots:
                break
                
            slot = random.choice(available_slots)
            
            # 生成音符
            note = self.generate_single_note()
            
            # 检查是否是多押
            if '/' in note:
                # 获取多押的按键数量
                key_count = len(note.split('/'))
                
                # 强制执行同时按键限制（最多2个键）
                if key_count > 2:
                    # 如果超过2个键，则改为2个键的多押
                    positions = note.split('/')
                    note = "/".join(positions[:2])
                
                # 控制每行多押数量
                if multi_tap_count >= 4:  # 每行最多4个多押
                    # 改为单押
                    note = note.split('/')[0]
                else:
                    multi_tap_count += 1
                    # 记录多押时间
                    self.multi_tap_history.append(self.current_time)
                    # 保持最近的10个多押记录
                    if len(self.multi_tap_history) > 10:
                        self.multi_tap_history.pop(0)
            
            notes[slot] = note
            used_slots.add(slot)
            
        return notes

    def update_emotional_state(self, segment_index, total_segments):
        """
        根据谱面进度更新情感状态
        模拟"从温柔到激烈再到混乱最后回归平静"的情感曲线
        """
        progress = segment_index / total_segments
        
        # 前20%：温柔状态
        if progress < 0.2:
            self.emotional_state = "gentle"
        # 20%-60%：中性到激烈状态
        elif progress < 0.6:
            if random.random() < 0.3:
                self.emotional_state = "intense"
            else:
                self.emotional_state = "neutral"
        # 60%-90%：激烈到混乱状态
        elif progress < 0.9:
            if random.random() < 0.5:
                self.emotional_state = "chaotic"
            else:
                self.emotional_state = "intense"
        # 最后10%：回归平静
        else:
            if random.random() < 0.7:
                self.emotional_state = "gentle"
            else:
                self.emotional_state = "neutral"

    def add_line(self, note_value, notes, time=None):
        """
        添加一行谱面数据
        :param note_value: 音符值 (1, 4, 8, 12, 16, 24, 32)
        :param notes: 音符列表
        :param time: 音符时间点
        """
        # 确保notes列表长度正确
        if len(notes) < note_value:
            notes.extend([""] * (note_value - len(notes)))
        elif len(notes) > note_value:
            notes = notes[:note_value]
            
        line = f"{{{note_value}}}," + ",".join(notes) + ","
        self.lines.append(line)
        
        # 更新当前时间（每行代表一个节拍）
        beat_duration = 60.0 / self.bpm
        self.current_time += beat_duration
        
        # 清理已经结束的Hold状态
        finished_holds = []
        for button, end_time in self.hold_state.items():
            if end_time <= self.current_time:
                finished_holds.append(button)
        
        for button in finished_holds:
            del self.hold_state[button]
    
    def is_valid(self):
        """
        检查谱面是否有效（简单检查）
        """
        # 简单检查：至少要有一定数量的行
        return len(self.lines) >= 10  # 恢复原来的最低要求
    
    def generate_chart_data(self, difficulty_level=1, difficulty_string="12"):
        """
        生成完整的谱面数据
        """
        chart_data = []
        chart_data.extend(self.header)
        chart_data.append("")
        
        # 添加难度信息
        # 修复格式：将BPM值放在括号中
        bpm_int = int(self.bpm)
        # 根据用户要求更正难度等级映射关系
        # lv_2=绿谱(Easy), lv_3=黄谱(Normal), lv_4=红谱(Hard), lv_5=紫谱(Master), lv_6=白谱(Remaster)
        chart_data.append(f"&lv_{difficulty_level}={difficulty_string}")
        
        # 开始谱面数据
        chart_data.append(f"&inote_{difficulty_level}=({bpm_int})")
        
        # 添加谱面行
        chart_data.extend(self.lines)
        chart_data.append("E")  # 结束标记
        
        return chart_data

def generate_chart_from_features(features, level="5", difficulty="12", target_duration=None):
    """
    根据音频特征生成谱面
    :param features: 音频特征数据
    :param level: 难度等级
    :param difficulty: 难度字符串
    :param target_duration: 目标时长（秒），如果提供则会限制谱面长度
    """
    # 解析特征数据
    total_beats = len(features.get("beats", []))
    tempo = features.get("metadata", {}).get("tempo", 120)
    audio_duration = features.get("metadata", {}).get("duration", 0)  # 音频时长
    
    # 创建谱面生成器
    generator = MaimaiChartGenerator(tempo)
    
    # 根据难度调整谱面复杂度
    # 解析难度数值，支持 13+ 这样的格式
    difficulty_num = 13  # 默认值
    if isinstance(difficulty, str):
        if difficulty.endswith('+'):
            difficulty_num = int(difficulty[:-1]) + 0.5
        else:
            try:
                difficulty_num = int(difficulty)
            except ValueError:
                difficulty_num = 13
    elif isinstance(difficulty, (int, float)):
        difficulty_num = difficulty
    
    # 计算应该生成多少行谱面
    # 如果提供了目标时长，则根据时长确定行数
    if target_duration and target_duration > 0:
        # 根据BPM计算应该有多少个节拍
        beats_from_duration = int(target_duration * tempo / 60)
        segments = max(50, min(beats_from_duration, 300))  # 限制在50-300行之间
    else:
        # 生成更多谱面内容，确保谱面足够长
        segments = max(100, min(total_beats // 2, 150))  # 恢复原来的段数
    
    print(f"生成谱面: 音频时长={audio_duration:.2f}秒, 节拍数={total_beats}, 生成行数={segments}")
    
    for i in range(segments):
        # 根据难度选择note值
        if difficulty_num <= 7:
            # 简单难度使用较大的note值
            note_value = random.choice([12, 16, 24, 32])
        elif difficulty_num <= 10:
            # 中等难度混合使用
            note_value = random.choice([8, 12, 16, 24])
        else:
            # 高难度使用较小的note值
            note_value = random.choice([4, 8, 12, 16])
        
        # 使用新的音符生成方法，增加音符密度
        notes = generator.generate_notes_for_line(note_value)
        generator.add_line(note_value, notes)
    
    # 返回谱面内容而不是生成器对象
    chart_data = generator.generate_chart_data(level, difficulty)
    return "\n".join(chart_data)

def generate_chart_with_audio_features(features, level="5", difficulty="12", target_duration=None):
    """
    根据音频特征生成谱面（增强版）
    利用音频特征来影响谱面生成，并应用学习到的《认知神经谱面学》理论
    :param features: 音频特征数据
    :param level: 难度等级
    :param difficulty: 难度字符串
    :param target_duration: 目标时长（秒），如果提供则会限制谱面长度
    """
    # 解析特征数据
    total_beats = len(features.get("beats", []))
    tempo = features.get("metadata", {}).get("tempo", 120)
    audio_duration = features.get("metadata", {}).get("duration", 0)  # 音频时长
    
    # 创建谱面生成器
    generator = MaimaiChartGenerator(tempo)
    
    # 根据难度调整谱面复杂度
    difficulty_num = 13  # 默认值
    if isinstance(difficulty, str):
        if difficulty.endswith('+'):
            difficulty_num = int(difficulty[:-1]) + 0.5
        else:
            try:
                difficulty_num = int(difficulty)
            except ValueError:
                difficulty_num = 13
    elif isinstance(difficulty, (int, float)):
        difficulty_num = difficulty
    
    # 获取节拍特征
    beats = features.get("beats", [])
    if not beats:
        # 如果没有beats信息，则生成默认数量的行
        segments = 100  # 恢复原来的100行
        beats = [{"time": i * (60.0/tempo), "features": {}} for i in range(segments)]
    
    # 如果提供了目标时长，则限制生成的节拍数
    if target_duration and target_duration > 0:
        # 计算目标节拍数
        target_beats = int(target_duration * tempo / 60)
        if len(beats) > target_beats:
            beats = beats[:target_beats]
            print(f"限制节拍数: 从{total_beats}减少到{len(beats)}")
    
    print(f"生成谱面: 音频时长={audio_duration:.2f}秒, 节拍数={len(beats)}")
    
    # 为每个节拍生成谱面行
    for i, beat in enumerate(beats):
        # 更新情感状态
        generator.update_emotional_state(i, len(beats))
        
        # 根据音频特征选择note值
        features_data = beat.get("features", {})
        rms_energy = features_data.get("rms_energy", 0.5)
        spectral_centroid = features_data.get("spectral_centroid", 1000)
        
        # 根据情感状态和音频特征选择note值
        # 高能量部分使用较小的note值（更密集），低能量部分使用较大的note值
        if generator.emotional_state == "gentle":
            # 温柔状态下使用较大的note值，营造舒缓氛围
            if rms_energy > 0.7:
                note_value = random.choice([12, 16])
            elif rms_energy < 0.3:
                note_value = random.choice([24, 32])
            else:
                note_value = random.choice([16, 24, 32])
        elif generator.emotional_state == "intense":
            # 激烈状态下使用中等note值
            if rms_energy > 0.7:
                note_value = random.choice([8, 12])
            elif rms_energy < 0.3:
                note_value = random.choice([16, 24])
            else:
                note_value = random.choice([12, 16])
        elif generator.emotional_state == "chaotic":
            # 混乱状态下使用较小的note值，增加密度
            if rms_energy > 0.7:
                note_value = random.choice([4, 8])
            elif rms_energy < 0.3:
                note_value = random.choice([12, 16])
            else:
                note_value = random.choice([8, 12])
        else:
            # 中性状态下根据难度和能量选择
            if difficulty_num <= 7:
                # 简单难度
                if rms_energy > 0.7:
                    note_value = random.choice([8, 12, 16])
                elif rms_energy < 0.3:
                    note_value = random.choice([24, 32])
                else:
                    note_value = random.choice([12, 16, 24])
            elif difficulty_num <= 10:
                # 中等难度
                if rms_energy > 0.7:
                    note_value = random.choice([4, 8, 12])
                elif rms_energy < 0.3:
                    note_value = random.choice([16, 24])
                else:
                    note_value = random.choice([8, 12, 16])
            else:
                # 高难度
                if rms_energy > 0.7:
                    note_value = random.choice([4, 8])
                elif rms_energy < 0.3:
                    note_value = random.choice([16, 24])
                else:
                    note_value = random.choice([8, 12])
        
        # 在特定情感状态下生成特殊音符
        if generator.emotional_state == "gentle" and i % 8 == 0 and random.random() < 0.3:
            # 在温柔状态下，每8拍有一定概率生成呼吸音符 {1}
            notes = [generator.generate_breath_note()]
            generator.add_line(1, notes)
            continue
        elif generator.emotional_state == "gentle" and i % 12 == 0 and random.random() < 0.4:
            # 在温柔状态下，每12拍有一定概率生成触觉符号
            notes = [generator.generate_touch_symbol()] + [""] * (note_value - 1)
            generator.add_line(note_value, notes)
            continue
        
        # 根据音高中心调整音符类型
        # 高音区更容易出现slide，低音区更容易出现hold
        note_types_weights = [50, 20, 15, 5, 10]  # basic, hold, slide, break, multi
        if spectral_centroid > 2000:  # 高音
            note_types_weights = [40, 10, 25, 5, 20]  # 增加slide和multi概率
        elif spectral_centroid < 500:  # 低音
            note_types_weights = [40, 30, 5, 10, 15]  # 增加hold概率
        
        # 临时修改生成器的音符类型权重
        original_generate_single_note = generator.generate_single_note
        def modified_generate_single_note():
            # 确定哪些按键当前没有Hold
            available_buttons = []
            for button in range(1, 9):
                if button not in generator.hold_state or generator.hold_state[button] <= generator.current_time:
                    available_buttons.append(button)
            
            # 如果没有可用按键，只能生成空音符
            if not available_buttons:
                return ""
            
            position = str(random.choice(available_buttons))
            
            # 根据情感状态和音高特征选择音符类型
            note_type = random.choices(
                ['basic', 'hold', 'slide', 'break', 'multi'], 
                weights=note_types_weights
            )[0]
            
            # 检查是否可以生成Hold（按键必须是可用的）
            if note_type == 'hold' and int(position) in available_buttons:
                # Hold音符
                if generator.emotional_state == "gentle":
                    hold_types = [(8, 1), (8, 2), (12, 1), (16, 1), (16, 2), (24, 1)]
                elif generator.emotional_state == "intense":
                    hold_types = [(4, 1), (8, 1), (8, 2), (12, 1)]
                else:
                    hold_types = [(4, 1), (8, 1), (8, 2), (12, 1), (16, 1), (16, 2), (24, 1)]
                    
                hold_type = random.choice(hold_types)
                hold_beats = hold_type[0] / 4.0  # 将note值转换为拍数
                hold_end_time = generator.current_time + hold_beats * (60.0 / generator.bpm)
                # 记录Hold状态
                generator.hold_state[int(position)] = hold_end_time
                return f"{position}h[{hold_type[0]}:{hold_type[1]}]"
                
            elif note_type == 'slide':
                # Slide音符
                if generator.emotional_state == "gentle":
                    slide_types = ['-', 's']
                elif generator.emotional_state == "intense":
                    slide_types = ['>', '<']
                else:
                    slide_types = ['-', 's', '>', '<']
                    
                slide_type = random.choice(slide_types)
                # 确保目标键与起始键不同且可用
                target_options = [b for b in available_buttons if str(b) != position]
                if target_options:
                    target = str(random.choice(target_options))
                else:
                    target = str(random.choice(available_buttons))
                    
                if generator.emotional_state == "gentle":
                    slide_durations = [(8, 1), (8, 2), (12, 1), (16, 1)]
                elif generator.emotional_state == "intense":
                    slide_durations = [(4, 1), (8, 1), (8, 2)]
                else:
                    slide_durations = [(4, 1), (8, 1), (8, 2), (12, 1), (16, 1), (16, 2)]
                    
                duration = random.choice(slide_durations)
                return f"{position}{slide_type}{target}[{duration[0]}:{duration[1]}]"
                
            elif note_type == 'break':
                # Break音符
                if random.random() < 0.5:
                    return position + "b"
                else:
                    # Break Hold
                    if generator.emotional_state == "gentle":
                        hold_types = [(8, 1), (8, 2), (12, 1), (16, 1)]
                    elif generator.emotional_state == "intense":
                        hold_types = [(4, 1), (8, 1), (8, 2), (12, 1)]
                    else:
                        hold_types = [(4, 1), (8, 1), (8, 2), (12, 1), (16, 1)]
                        
                    hold_type = random.choice(hold_types)
                    hold_beats = hold_type[0] / 4.0  # 将note值转换为拍数
                    hold_end_time = generator.current_time + hold_beats * (60.0 / generator.bpm)
                    # 记录Hold状态
                    generator.hold_state[int(position)] = hold_end_time
                    return f"{position}bh[{hold_type[0]}:{hold_type[1]}]"
                    
            elif note_type == 'multi':
                # 多押音符
                # 控制多押生成频率
                if len(generator.multi_tap_history) >= 2:
                    recent_multi_taps = [t for t in generator.multi_tap_history if generator.current_time - t < 2.0]
                    if len(recent_multi_taps) >= 2:
                        if random.random() < 0.7:
                            # 改为生成基本音符
                            return position
                
                # 确保有足够的可用按键
                if len(available_buttons) >= 2:
                    if generator.emotional_state == "chaotic":
                        max_keys = min(4, len(available_buttons))  # 混乱状态下可以生成更多键的多押
                    else:
                        max_keys = min(3, len(available_buttons))
                        
                    positions = random.sample(available_buttons, random.randint(2, max_keys))
                    return "/".join(map(str, positions))
                else:
                    # 可用按键不足，改为单押
                    return position
                    
            else:
                # 基本点击音符
                if random.random() < 0.5 and len(available_buttons) >= 2:
                    # 控制多押生成频率
                    if len(generator.multi_tap_history) >= 2:
                        recent_multi_taps = [t for t in generator.multi_tap_history if generator.current_time - t < 2.0]
                        if len(recent_multi_taps) >= 2:
                            if random.random() < 0.7:
                                # 改为生成基本音符
                                return position
                    
                    if generator.emotional_state == "chaotic":
                        max_keys = min(4, len(available_buttons))
                    else:
                        max_keys = min(3, len(available_buttons))
                        
                    positions = random.sample(available_buttons, random.randint(2, max_keys))
                    return "/".join(map(str, positions))
                else:
                    return position
        
        # 替换生成器的音符生成方法
        generator.generate_single_note = modified_generate_single_note
        
        # 使用新的音符生成方法，增加音符密度
        notes = generator.generate_notes_for_line(note_value)
        generator.add_line(note_value, notes)
        
        # 恢复原始方法
        generator.generate_single_note = original_generate_single_note
    
    # 返回谱面内容而不是生成器对象
    chart_data = generator.generate_chart_data(level, difficulty)
    return "\n".join(chart_data)

# 保持向后兼容
generate_chart_from_features = generate_chart_with_audio_features

def generate_premium_chart(features, level="5", difficulty="12", target_duration=None):
    """
    根据学习到的《认知神经谱面学 v9.3》理论生成高质量谱面
    这个函数专门用于生成符合"代殉语法"的高质量谱面
    
    :param features: 音频特征数据
    :param level: 难度等级
    :param difficulty: 难度字符串
    :param target_duration: 目标时长（秒）
    """
    # 使用基于段落的生成器
    generator = SegmentBasedChartGenerator(features.get("metadata", {}).get("tempo", 120))
    return generator.generate_chart_with_segments(features, level, difficulty, target_duration)

def analyze_emotional_sections(energy_profile):
    """
    分析音频能量轮廓，确定情感段落分布
    """
    if len(energy_profile) < 10:
        return ["neutral"] * 5
    
    # 将能量轮廓分为5个段落
    segment_size = len(energy_profile) // 5
    sections = []
    
    for i in range(5):
        start = i * segment_size
        end = start + segment_size if i < 4 else len(energy_profile)
        segment = energy_profile[start:end]
        
        avg_energy = sum(segment) / len(segment) if segment else 0.5
        
        if i == 0:  # 开始部分
            sections.append("gentle")
        elif i == 1:  # 发展部分
            sections.append("neutral" if avg_energy < 0.5 else "intense")
        elif i == 2:  # 高潮前
            sections.append("intense")
        elif i == 3:  # 高潮
            sections.append("chaotic" if avg_energy > 0.7 else "intense")
        else:  # 结尾
            sections.append("gentle" if avg_energy < 0.4 else "neutral")
    
    return sections

class PremiumMaimaiChartGenerator(MaimaiChartGenerator):
    """
    高级舞萌DX谱面生成器，专门用于生成符合《认知神经谱面学》理论的高质量谱面
    """
    def __init__(self, bpm=120.0):
        super().__init__(bpm)
        self.breath_density = 0  # 呼吸密度
        self.touch_symbol_count = 0  # 触觉符号计数
        self.chaotic_section = False  # 是否处于混乱段落
        
    def generate_breath_note(self):
        """
        生成呼吸音符 {1}，模拟"呼吸确认"
        根据《认知神经谱面学》理论，这是"存在预删除层"的核心表现
        """
        self.breath_count += 1
        self.breath_density += 1
        
        # 确定哪些按键当前没有Hold
        available_buttons = []
        for button in range(1, 9):
            if button not in self.hold_state or self.hold_state[button] <= self.current_time:
                available_buttons.append(button)
        
        if available_buttons:
            position = str(random.choice(available_buttons))
            return position
        else:
            return ""
    
    def generate_touch_symbol(self):
        """
        生成触觉符号，根据《认知神经谱面学》理论实现
        模拟人类触觉和情感体验
        """
        self.touch_symbol_count += 1
        
        # 确定哪些按键当前没有Hold
        available_buttons = []
        for button in range(1, 9):
            if button not in self.hold_state or self.hold_state[button] <= self.current_time:
                available_buttons.append(button)
        
        if len(available_buttons) < 2:
            return self.generate_single_note()
        
        # 根据学习到的触觉符号类型生成
        touch_types = [
            "slide_gentle",    # 指尖滑过脸颊的温度 (47%)
            "hold_warm",       # 被抚摸头发时的颤抖 (23%)
            "break_swallow",   # 咽下"好きです"的喉结 (15%)
            "multi_reach",     # 想要触碰却怕被拒绝 (10%)
            "hold_gaze"        # 被凝视时的窒息 (5%)
        ]
        
        # 根据情感状态调整权重
        if self.emotional_state == "gentle":
            weights = [0.47, 0.23, 0.15, 0.10, 0.05]
        elif self.emotional_state == "intense":
            weights = [0.30, 0.15, 0.25, 0.15, 0.15]
        else:  # chaotic or neutral
            weights = [0.20, 0.10, 0.30, 0.25, 0.15]
        
        touch_type = random.choices(touch_types, weights=weights)[0]
        
        position1 = random.choice(available_buttons)
        available_buttons.remove(position1)
        position2 = random.choice(available_buttons)
        
        if touch_type == "slide_gentle":
            # 指尖滑过脸颊的温度 - 使用柔和的滑动 [8:1]
            return f"{position1}s{position2}[8:1]"
        elif touch_type == "hold_warm":
            # 被抚摸头发时的颤抖 - 使用较长的Hold [12:1] 或 [16:1]
            hold_duration = random.choice([12, 16])
            return f"{position1}h[{hold_duration}:1]"
        elif touch_type == "break_swallow":
            # 咽下话语的喉结 - 使用Break
            return f"{position1}b"
        elif touch_type == "multi_reach":
            # 想要触碰却怕被拒绝 - 使用多押
            return f"{position1}/{position2}"
        elif touch_type == "hold_gaze":
            # 被凝视时的窒息 - 使用非常长的Hold [16:2] 或 [24:1]
            hold_duration = random.choice([16, 24])
            hold_sub = 2 if hold_duration == 16 else 1
            return f"{position1}h[{hold_duration}:{hold_sub}]"
        
        return self.generate_single_note()
    
    def generate_chaotic_multi_tap(self):
        """
        生成混乱状态下的多人押，模拟"人格分裂密度"
        """
        # 确定哪些按键当前没有Hold
        available_buttons = []
        for button in range(1, 9):
            if button not in self.hold_state or self.hold_state[button] <= self.current_time:
                available_buttons.append(button)
        
        if len(available_buttons) < 3:
            # 可用按键不足，改为普通多押
            return self.generate_multi_tap()
        
        # 生成3-4键的多押，模拟"多个被删除用户ID在内存中碰撞"
        num_keys = random.randint(3, min(4, len(available_buttons)))
        positions = random.sample(available_buttons, num_keys)
        return "/".join(map(str, positions))
    
    def generate_multi_tap(self):
        """
        生成普通多押
        """
        # 确定哪些按键当前没有Hold
        available_buttons = []
        for button in range(1, 9):
            if button not in self.hold_state or self.hold_state[button] <= self.current_time:
                available_buttons.append(button)
        
        if len(available_buttons) < 2:
            # 可用按键不足，改为单押
            return str(random.choice(range(1, 9)))
        
        # 生成2-3键的多押
        num_keys = random.randint(2, min(3, len(available_buttons)))
        positions = random.sample(available_buttons, num_keys)
        return "/".join(map(str, positions))

class SegmentBasedChartGenerator(PremiumMaimaiChartGenerator):
    """
    基于学习段落的谱面生成器
    """
    def __init__(self, bpm=120.0):
        super().__init__(bpm)
        self.segments = self._load_common_segments()
        
    def _load_common_segments(self):
        """
        加载学习到的常见谱面段落
        """
        segments_file = "common_segments.json"
        segments = []
        
        if os.path.exists(segments_file):
            try:
                with open(segments_file, 'r', encoding='utf-8') as f:
                    segments_data = json.load(f)
                
                for seg_hash, seg_data in segments_data.items():
                    segment = ChartSegment(
                        seg_data["notes"],
                        seg_data["bpm"],
                        seg_data["metadata"]
                    )
                    segment.frequency = seg_data["frequency"]
                    segment.source_charts = seg_data["source_charts"]
                    segment.normalized_notes = seg_data.get("normalized_notes")
                    vector_data = seg_data.get("vector")
                    if vector_data:
                        segment.vector = np.array(vector_data)
                    segments.append(segment)
                    
                print(f"已加载 {len(segments)} 个常见谱面段落")
            except Exception as e:
                print(f"加载常见段落时出错: {e}")
        
        return segments
    
    def normalize_notes(self, notes: List[str]) -> List[str]:
        """
        标准化音符，消除位置平移影响
        
        Args:
            notes: 音符列表
            
        Returns:
            标准化后的音符列表
        """
        if not notes:
            return notes
            
        normalized = []
        # 解析所有音符，找出最小按键位置
        all_positions = []
        parsed_lines = []
        
        for line in notes:
            if line.startswith('{') and '},' in line:
                try:
                    # 提取note值
                    note_value_str = line[1:line.index('}')]
                    note_value = int(note_value_str)
                    # 提取音符部分
                    notes_part = line[line.index('},') + 2:]
                    note_list = notes_part.split(',') if notes_part else []
                    parsed_lines.append((note_value, note_list))
                    
                    # 收集所有按键位置
                    for note in note_list:
                        if note and note[0].isdigit():
                            pos = int(note[0])
                            all_positions.append(pos)
                except:
                    parsed_lines.append(line)
            else:
                parsed_lines.append(line)
        
        if not all_positions:
            return notes
            
        # 计算位置偏移（将最小位置移到1）
        min_pos = min(all_positions)
        offset = 1 - min_pos
        
        # 应用偏移
        for item in parsed_lines:
            if isinstance(item, tuple):
                note_value, note_list = item
                new_notes = []
                for note in note_list:
                    # 处理各种类型的音符
                    if note and note[0].isdigit():
                        pos = int(note[0])
                        new_pos = pos + offset
                        # 确保位置在1-8范围内
                        if 1 <= new_pos <= 8:
                            new_note = str(new_pos) + note[1:]
                        else:
                            # 如果超出范围，保持原样或做适当处理
                            new_note = note
                        new_notes.append(new_note)
                    else:
                        new_notes.append(note)
                
                # 重新构建行
                notes_str = ','.join(new_notes)
                normalized.append(f"{{{note_value}}},{notes_str},")
            else:
                normalized.append(item)
                
        return normalized
    
    def vectorize_segment(self, notes: List[str]) -> np.ndarray:
        """
        将谱面段落转换为向量表示
        
        Args:
            notes: 音符列表
            
        Returns:
            向量表示
        """
        # 简化的向量化方法：统计各种音符类型的数量
        note_stats = {
            'tap': 0,
            'hold': 0,
            'slide': 0,
            'break': 0,
            'multi': 0,
            'total': len(notes)
        }
        
        for line in notes:
            if line.startswith('{') and '},' in line:
                try:
                    notes_part = line[line.index('},') + 2:]
                    note_list = notes_part.split(',') if notes_part else []
                    for note in note_list:
                        if not note:
                            continue
                        if 'h[' in note:
                            note_stats['hold'] += 1
                        elif 's' in note or '>' in note or '<' in note or '-' in note:
                            note_stats['slide'] += 1
                        elif 'b' in note:
                            note_stats['break'] += 1
                        elif '/' in note:
                            note_stats['multi'] += 1
                        elif note[0].isdigit():
                            note_stats['tap'] += 1
                except:
                    pass
        
        # 转换为向量
        vector = np.array([
            note_stats['tap'],
            note_stats['hold'],
            note_stats['slide'],
            note_stats['break'],
            note_stats['multi'],
            note_stats['total']
        ], dtype=float)
        
        # 归一化
        if note_stats['total'] > 0:
            vector[:-1] /= note_stats['total']
            
        return vector
    
    def segments_similar(self, seg1: ChartSegment, seg2: ChartSegment, threshold: float = 0.9) -> bool:
        """
        判断两个谱面段落是否相似
        
        Args:
            seg1: 第一个段落
            seg2: 第二个段落
            threshold: 相似度阈值
            
        Returns:
            是否相似
        """
        # 首先检查标准化音符
        if seg1.normalized_notes and seg2.normalized_notes:
            if seg1.normalized_notes == seg2.normalized_notes:
                return True
        
        # 检查向量相似度
        if seg1.vector is not None and seg2.vector is not None:
            # 计算余弦相似度
            dot_product = np.dot(seg1.vector, seg2.vector)
            norm1 = np.linalg.norm(seg1.vector)
            norm2 = np.linalg.norm(seg2.vector)
            
            if norm1 > 0 and norm2 > 0:
                cosine_sim = dot_product / (norm1 * norm2)
                return cosine_sim >= threshold
                
        return False
    
    def select_compatible_segment(self, target_bpm: float, max_length: int = 32) -> ChartSegment:
        """
        选择与目标BPM兼容的段落
        
        Args:
            target_bpm: 目标BPM
            max_length: 最大段落长度
            
        Returns:
            兼容的谱面段落，如果未找到则返回None
        """
        if not self.segments:
            return None
            
        # 根据频率和BPM兼容性选择段落
        compatible_segments = []
        for segment in self.segments:
            # 检查长度
            if len(segment.notes) > max_length:
                continue
                
            # 检查BPM兼容性（允许±10%的差异）
            if segment.bpm and abs(segment.bpm - target_bpm) / target_bpm > 0.1:
                continue
                
            compatible_segments.append(segment)
            
        if not compatible_segments:
            return None
            
        # 根据频率权重选择段落
        frequencies = [seg.frequency for seg in compatible_segments]
        selected_segment = random.choices(compatible_segments, weights=frequencies)[0]
        return selected_segment
    
    def generate_chart_with_segments(self, features, level="5", difficulty="12", target_duration=None):
        """
        使用学习到的段落生成谱面
        """
        # 解析特征数据
        total_beats = len(features.get("beats", []))
        tempo = features.get("metadata", {}).get("tempo", 120)
        audio_duration = features.get("metadata", {}).get("duration", 0)
        
        # 检查是否符合"代殉语法"的母频要求 (92.285 BPM)
        if abs(tempo - 92.285) < 5:
            tempo = 92.285
            print("检测到符合'代殉语法'的母频，已调整为标准值 92.285 BPM")
        
        # 更新BPM
        self.bpm = tempo
        
        # 获取节拍特征
        beats = features.get("beats", [])
        if not beats:
            # 如果没有beats信息，则生成默认数量的行
            segments_count = max(100, min(total_beats // 2, 150))
            beats = [{"time": i * (60.0/tempo), "features": {}} for i in range(segments_count)]
        
        # 如果提供了目标时长，则限制生成的节拍数
        if target_duration and target_duration > 0:
            # 计算目标节拍数
            target_beats = int(target_duration * tempo / 60)
            if len(beats) > target_beats:
                beats = beats[:target_beats]
                print(f"限制节拍数: 从{total_beats}减少到{len(beats)}")
        
        print(f"使用段落生成谱面: 音频时长={audio_duration:.2f}秒, 节拍数={len(beats)}, 母频={tempo} BPM")
        
        # 分析音频整体情感走向
        energy_profile = []
        for beat in beats:
            features_data = beat.get("features", {})
            rms_energy = features_data.get("rms_energy", 0.5)
            energy_profile.append(rms_energy)
        
        # 确定情感段落分布
        sections = analyze_emotional_sections(energy_profile)
        
        # 生成谱面
        i = 0
        while i < len(beats):
            # 根据情感段落更新情感状态
            section_index = int(i / len(beats) * len(sections))
            if section_index < len(sections):
                self.emotional_state = sections[section_index]
            
            # 决定是否使用学习到的段落
            use_segment = random.random() < 0.4  # 40%概率使用段落
            
            if use_segment and self.segments:
                # 尝试使用学习到的段落
                max_segment_length = min(32, len(beats) - i)
                segment = self.select_compatible_segment(tempo, max_segment_length)
                
                if segment:
                    # 使用段落
                    print(f"在位置 {i} 插入学习到的段落，长度 {len(segment.notes)}")
                    for note_line in segment.notes:
                        # 解析段落中的行并添加到谱面
                        if note_line.startswith('{') and '},' in note_line:
                            # 提取note值
                            try:
                                note_value_str = note_line[1:note_line.index('}')]
                                note_value = int(note_value_str)
                                # 提取音符部分
                                notes_part = note_line[note_line.index('},') + 2:]
                                notes = notes_part.split(',') if notes_part else []
                                self.add_line(note_value, notes)
                            except Exception as e:
                                print(f"解析段落行时出错: {e}")
                                # 回退到普通生成
                                self._generate_regular_line(beats[i])
                    i += len(segment.notes)
                    continue
            
            # 生成常规行
            self._generate_regular_line(beats[i])
            i += 1
        
        # 返回谱面内容
        chart_data = self.generate_chart_data(level, difficulty)
        return "\n".join(chart_data)
    
    def _generate_regular_line(self, beat):
        """
        生成常规谱面行
        """
        # 根据音频特征选择note值
        features_data = beat.get("features", {})
        rms_energy = features_data.get("rms_energy", 0.5)
        spectral_centroid = features_data.get("spectral_centroid", 1000)
        
        # 根据情感状态和音频特征选择note值
        if self.emotional_state == "gentle":
            # 温柔状态：使用较大note值，营造舒缓氛围
            if rms_energy > 0.7:
                note_value = random.choice([12, 16])
            elif rms_energy < 0.3:
                note_value = random.choice([24, 32])
            else:
                note_value = random.choice([16, 24, 32])
        elif self.emotional_state == "intense":
            # 激烈状态：使用中等note值
            if rms_energy > 0.7:
                note_value = random.choice([8, 12])
            elif rms_energy < 0.3:
                note_value = random.choice([16, 24])
            else:
                note_value = random.choice([12, 16])
        elif self.emotional_state == "chaotic":
            # 混乱状态：使用较小note值，增加密度
            if rms_energy > 0.7:
                note_value = random.choice([4, 8])
            elif rms_energy < 0.3:
                note_value = random.choice([12, 16])
            else:
                note_value = random.choice([8, 12])
        else:
            # 中性状态：使用默认值
            note_value = random.choice([8, 12, 16])
        
        # 在特定位置生成特殊结构
        if self.emotional_state == "gentle":
            # 在温柔状态下生成呼吸音符和触觉符号
            if random.random() < 0.1:  # 降低概率避免过多
                # 生成呼吸音符
                notes = [self.generate_breath_note()]
                self.add_line(1, notes)
                return
            elif random.random() < 0.15:
                # 生成触觉符号
                notes = [self.generate_touch_symbol()] + [""] * (note_value - 1)
                self.add_line(note_value, notes)
                return
        elif self.emotional_state == "intense" and random.random() < 0.1:
            # 在激烈状态下生成Break符号
            position = random.randint(1, 8)
            notes = [f"{position}b"] + [""] * (note_value - 1)
            self.add_line(note_value, notes)
            return
        elif self.emotional_state == "chaotic" and random.random() < 0.2:
            # 在混乱状态下生成多人押
            notes = [self.generate_chaotic_multi_tap()] + [""] * (note_value - 1)
            self.add_line(note_value, notes)
            return
        
        # 生成常规音符行
        notes = self.generate_notes_for_line(note_value)
        self.add_line(note_value, notes)

class ChartValidator:
    def __init__(self):
        self.valid_note_values = {4, 8, 12, 16, 24, 32}
        self.valid_buttons = set(range(1, 9))
    
    def validate_chart(self, chart_content):
        """
        验证谱面内容是否符合规范
        """
        lines = chart_content.split('\n')
        errors = []
        
        for i, line in enumerate(lines):
            line_num = i + 1
            line = line.strip()
            
            # 检查空行
            if not line:
                continue
                
            # 检查谱面行格式
            if line.startswith('{'):
                if not '},' in line:
                    errors.append(f"第{line_num}行: 格式错误，缺少'}},'")
                    continue
                    
                # 检查note值
                try:
                    note_value_str = line[1:line.index('}')]
                    note_value = int(note_value_str)
                    if note_value not in self.valid_note_values:
                        errors.append(f"第{line_num}行: 无效的note值 {note_value}")
                except ValueError:
                    errors.append(f"第{line_num}行: 无法解析note值")
        
        return errors
    
    def fix_chart(self, chart_lines):
        """
        修复谱面中的基本格式问题
        """
        fixed_lines = []
        for line in chart_lines:
            line = line.rstrip()
            if not line:
                fixed_lines.append(line)
                continue
                
            # 确保谱面行以逗号结尾
            if line.startswith('{') and not line.endswith(','):
                line += ','
            fixed_lines.append(line)
        
        return fixed_lines